Python antipattern: Close in finally

Don’t do this:

thing = Thing()
try:
    thing.do_stuff()
finally:
    thing.close()

Do do this:

from contextlib import closing

with closing(Thing()) as thing:
    thing.do_stuff()

Why is the second better? Using contextlib.closing() ties closing the item to its creation. These baby examples are about equally easy to reason about, with only a single line in the try block, but consider what happens ifwhen more lines get added in future? In the first example, the close moves away, potentially offscreen, but that doesn’t happen in the second.

GitLab YAML Docker Registry client

Have you written a Docker Registry API client in GitLab CI/CD YAML? I have.

# Delete candidate image from CI repository.
clean-image:
  stage: .post
  except:
    - main

  variables:
    AUTH_API: "$CI_SERVER_URL/jwt/auth"
    SCOPE: "repository:$CI_PROJECT_PATH"
    REGISTRY_API: "https://$CI_REGISTRY/v2/$CI_PROJECT_PATH"

  before_script:
    - >
      which jq >/dev/null
      || (sudo apt-get update
      && sudo apt-get -y install jq)

  script:
    - echo "Deleting $CANDIDATE_IMAGE"
    - >
      TOKEN=$(curl -s
      -u "$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD"
      "$AUTH_API?service=container_registry&scope=$SCOPE:delete,pull"
      | jq -r .token)
    - >
      DIGEST=$(curl -s -I
      -H "Authorization: Bearer $TOKEN"
      -H "Accept: application/vnd.docker.distribution.manifest.v2+json"
      "$REGISTRY_API/manifests/$CI_COMMIT_SHORT_SHA"
      | tr -d "\r"
      | grep -i "^docker-content-digest: "
      | sed "s/^[^:]*: *//")
    - >
      curl -s
      -X DELETE
      -H "Authorization: Bearer $TOKEN"
      "$REGISTRY_API/manifests/"$(echo $DIGEST | sed "s/:/%3A/g")

Use stdbuf to tee without buffering

Do you want to tee the output of a command to a file, but see it in your terminal too, without buffering? The stdbuf command can do this for you:

$ sudo stdbuf --output=L tcpdump -i any -tttt -n 'udp port 5353' | tee -a tcpdump-mdns
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
2023-06-15 11:55:16.637670 eth0 M   IP 10.0.0.23.5353 > 224.0.0.251.5353: 0 A (QM)? winnebago.local. (28)
2023-06-15 11:55:16.744660 eth0 M   IP 10.0.0.42.5353 > 224.0.0.251.5353: 0*- [0q] 1/0/0 (Cache flush) A 10.0.0.42 (38)
...

Which uninstalled package provides a file?

$ apt-file find guestmount
guestmount: /usr/bin/guestmount
guestmount: /usr/share/bash-completion/completions/guestmount
guestmount: /usr/share/doc/guestmount/changelog.Debian.gz
guestmount: /usr/share/doc/guestmount/copyright
guestmount: /usr/share/man/ja/man1/guestmount.1.gz
guestmount: /usr/share/man/man1/guestmount.1.gz
guestmount: /usr/share/man/uk/man1/guestmount.1.gz